#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <unordered_map>
#include <string>
#include <fcntl.h>

// ubuntu@VM-8-16-ubuntu:~/113/linux/linux_system/linux_-system/myshell$
#define _COMMAND_SIZE 1024
#define FORMAT "%s@%s:%s# "
#define MAXARGC 128
#define MAX_ENVS 1024
// 1、命令行参数表
// 全局数据
char *g_argv[MAXARGC];
int g_argc = 0;
// 2、环境变量表
char *g_env[MAX_ENVS];
int g_envs = 0;
// last exit code
int lastcode = 0;
// Cwd
char cwd[1024];
char cwdenv[1024*1024*2];

// 3、别名映射表
std::unordered_map<std::string, std::string> alias_list;
//重定向
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3

int redir = NONE_REDIR;
std::string filename;

const char *GetUserName()
{
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}
const char *GetHostName()
{
    char hostname[_COMMAND_SIZE];
    int result;
    result = gethostname(hostname, sizeof(hostname));
    return result == 0 ? hostname : "None";
}
const char *GetPwd()
{
    //const char* pwd = getenv("PWD");
    const char *pwd = getcwd(cwd, sizeof(cwd));
    if (pwd != NULL)
    {
        int tmp = snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        if(tmp < 0)
        {
            std::cout << "Error" <<std::endl;
        }
        else if(tmp >= sizeof(cwdenv))
        {
            tmp = sizeof(cwdenv);
        }
        putenv(cwdenv);
    }

    return pwd == NULL ? "None" : pwd;
}
const char *GetHome()
{
    const char *home = getenv("HOME");
    return home == NULL ? "" : home;
}
std::string DirName(const char *pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if (dir == SLASH)
        return SLASH;
    auto pos = dir.rfind(SLASH);
    if (pos == std::string::npos)
        return "BUG!!";
    return dir.substr(pos + 1);
}
void MakeCommandLine(char cmd_prompt[], int size)
{
    //snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), GetPwd());
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}
void PrintCommandPrompt()
{
    char prompt[_COMMAND_SIZE];
    MakeCommandLine(prompt, sizeof(prompt));
    printf("%s", prompt);
    fflush(stdout);
}
// ls -a -l->"ls -a -l\n" 字符串
bool GetCommandLine(char *out, int size)
{
    char *c = fgets(out, size, stdin);
    if (c == NULL)
        return false;
    if (strlen(out) == 0)
        return false;
    out[strlen(out) - 1] = 0; // 清理
    return true;
}

// 3.命令行解析 “ls -a -l” -> "ls" "-a" "-l
bool CommandPrase(char *commandline)
{
#define SEP " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline, SEP);
    while (g_argv[g_argc++] = strtok(nullptr, SEP))
        ;
    g_argc--;
    return g_argc > 0?true:false;
}
void PrintArgv()
{
    for (int i = 0; g_argv[i]; i++)
    {
        printf("g_argv[%d]->%s\n", i, g_argv[i]);
    }
    printf("argc:%d\n", g_argc);
}
// 执行命令
int Execute()
{
    pid_t id = fork();
    if (id == 0)
    {
        //子进程检测重定向
        int fd = -1;
        if(redir == INPUT_REDIR)
        {
            fd = open(filename.c_str(), O_RDONLY);
            if(fd < 0)
            {
                exit(1);
            }
            dup2(fd, 0);
            close(fd);
        }
        else if(redir == OUTPUT_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
            if(fd <0)
            {
                exit(2);
            }
            dup2(fd,1);
            close(fd);
        }
        else if(redir == APPEND_REDIR)
        {
            fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
            if(fd <0)
            {
                exit(2);
            }
            dup2(fd,1);
            close(fd);
        }
        else
        {
            //todo
        }
        // child
        execvp(g_argv[0], g_argv);
        exit(1);
    }
    // father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    //(void)rid;//rid使用一下
    if (rid > 0)
    {
        lastcode = WEXITSTATUS(status); // 提取状态码
    }
    return 0;
}
// command
bool Cd()
{
    // cd argc = 1
    if (g_argc == 1)
    {
        std::string home = GetHome();
        if (home.empty())
            return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where = g_argv[1];
        if (where == "-")
        {
            // todo
        }
        else if (where == "~")
        {
            // todo
        }
        else
        {
            chdir(where.c_str());
        }
    }
    return true;
}
bool Echo()
{
    // echo "word"
    // echo $？
    // echo $PATH
    std::string op = g_argv[1];
    if (op == "$?")
    {
        std::cout << lastcode << std::endl;
        lastcode = 0;
    }
    else if (op[0] == '$')
    {
        std::string env_name = op.substr(1);
        const char *env_value = getenv(env_name.c_str());
        if (env_value)
            std::cout << env_value << std::endl;
    }
    else
    {
        std::cout << op << std::endl;
    }
    return true;
}
// 检测是否是内建命令
//如果内建命令做重定向，要求更改shell的标准输入、标准输出、标准错误
bool DetectBuiltInCommand()
{
    std::string cmd = g_argv[0];
    if (cmd == "cd")
    {
        Cd();
        return true;
    }
    else if (cmd == "echo")
    {
        Echo();
        return true;
    }
    else if (cmd == "export")
    {
    }
    else if (cmd == "alias")
    {
        // std::string nickname = g_argv[1];
        // alias_list.insert(k, v);
    }
    return false;
}
// 初始化子进程环境变量表
void InitEnv()
{
    extern char **environ;
    memset(g_env, 0, sizeof(g_env));
    g_envs = 0;
    // 本来要从配置文件来
    // 1、获取环境变量
    // 申请空间
    for (int i = 0; environ[i]; i++)
    {
        g_env[i] = (char *)malloc(strlen(environ[i]) + 1);
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }
    g_env[g_envs++] = (char *)"HAHA=for_test";
    g_env[g_envs] = NULL;
    // 2、导入环境变量
    for (int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}
void TrimeSpace(char cmd[], int& end)
{
    while(isspace(cmd[end]))
    {
        end++;
    }
}
void RedirCheck(char cmd[])
{
    //初始化
    redir = NONE_REDIR;
    filename.clear();
    int start =0;
    int end = strlen(cmd) -1;
    while(start < end)
    {
        //ls -a -l < log.txt
        if(cmd[end] == '<')
        {
            cmd[end++] = 0;
            TrimeSpace(cmd, end);//处理连续空格情况
            redir = INPUT_REDIR;
            filename = cmd + end;
            break;
        }
        else if(cmd[end] == '>')
        {
            //ls -a -l > log.txt
            //ls -a -l >> log.txt
            if(cmd[end-1] == '>')
            {
                cmd[end-1] = 0;
                redir = APPEND_REDIR;
            }
            else
            {
                redir = OUTPUT_REDIR;
            }
            cmd[end++] = 0;
            TrimeSpace(cmd, end);//处理连续空格情况
            filename = cmd+end;
            break;
        }
        else
        {
            end--;
        }
    }
}
int main()
{
    //shell 启动的时候，从系统中获取环境变量
    //本shell的环境变量信息从父进程shell继承来
    InitEnv();
    while (true)
    {
        // 1.输出命令行提示符
        PrintCommandPrompt();
        // 2.获取用户输入的命令
        char commandline[_COMMAND_SIZE];
        if (!GetCommandLine(commandline, sizeof(commandline)))
            continue;
        //3.重定向分析 “ls -a -l > log.txt” ->"ls" "-a" "-l" "log.txt"判断重定向的方式
        RedirCheck(commandline);
        // 4.命令行解析 “ls -a -l” -> "ls" "-a" "-l"
        if (!CommandPrase(commandline))
            continue;
        // 打印参数
        //PrintArgv();
        // 检测别名
        // 5.检测并处理内建命令
        if (DetectBuiltInCommand())
            continue;
        // 6.执行命令
        Execute();
    }
    //clean()
    return 0;
}
